package com.fitz.proxy;

import com.fitz.annotation.RpcReference;
import com.fitz.exception.RpcException;
import com.fitz.registry.InstanceRegistry;
import com.fitz.registry.RegistryContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Proxy;

/**
 * @author fitz 2020-5-11 16:33
 */

public class ProxyFactory extends InstantiationAwareBeanPostProcessorAdapter implements InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(ProxyFactory.class);

    private InstanceRegistry instanceRegistry;

    private int minPoolSize;

    private int maxPoolSize;

    private byte serializerType;

    public ProxyFactory(int minPoolSize,int maxPoolSize,byte serializerType) {
        this.minPoolSize = minPoolSize;
        this.maxPoolSize = maxPoolSize;
        this.serializerType = serializerType;
        instanceRegistry = RegistryContext.registry;
    }

    /**
     * @desc: 创建代理对象
     * @author: fitz
     * @Date: 2020/5/27
     * @param: [interfaceClass, referenceBean]
     * @return: java.lang.Object
     **/
    private Object create(Class<?> interfaceClass,RpcReferenceBean referenceBean){
        if (interfaceClass == null){
            throw new RpcException("Class type cannot be null");
        }
        return Proxy.newProxyInstance(interfaceClass.getClassLoader(),
                new Class<?>[]{interfaceClass},new RpcProxyInvocationHandler(referenceBean));
    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }

    /**
     * @desc: 过滤
     * @author: fitz
     * @Date: 2020/5/27
     * @param: [bean, beanName]
     * @return: java.lang.Object
     **/
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("beanName: "+ beanName);
        ReflectionUtils.doWithFields(bean.getClass(), field -> {
            if (field.isAnnotationPresent(RpcReference.class)){
                Class interfaceClazz = field.getType();
                if (!interfaceClazz.isInterface()){
                    throw new RpcException("RpcReference must be interface.");
                }
                RpcReference rpcReference = field.getAnnotation(RpcReference.class);
                logger.info("RpcReference:{} initialize",interfaceClazz.getName());
                RpcReferenceBean referenceBean = new RpcReferenceBean(rpcReference.retry(), rpcReference.loadBalance());
                referenceBean.setInterfaceName(interfaceClazz.getName());
                referenceBean.setTimeout(rpcReference.timeout());
//                referenceBean.setInstance(new InstanceInfo());
                referenceBean.setClusterStrategy(rpcReference.clusterStrategy().clusterStrategy);
                referenceBean.setInstanceRegistry(instanceRegistry);
                ConnectPoolParam poolParam = new ConnectPoolParam();
                poolParam.setMinPoolSize(minPoolSize);
                poolParam.setMaxPoolSize(maxPoolSize);
                poolParam.setSerializerType(serializerType);
                referenceBean.setConnectPoolParam(poolParam);
                Object proxyBean = create(interfaceClazz,referenceBean);
                field.setAccessible(true);
                field.set(bean,proxyBean);
            }
        });

        return super.postProcessAfterInitialization(bean, beanName);
    }
}
